package Game;

import java.util.Random;
import java.util.Vector;

import org.eclipse.jetty.websocket.WebSocket.Connection;

import Joueur.ActionPointException;
import Joueur.GroupPlayer;
import Joueur.GroupPlayer.GroupIterator;
import Joueur.Player;
import Map.Map;
import Map.Tuile;
import Unite.AttributeMap;
import Unite.FactoryUnit;
import Unite.Legion;
import Unite.Unit;

public class Game {
	private static int ID_GAME = 0;
	private int _id;

	private int _nbTilesX;
	private int _nbTilesY;
	private Map _map;
	private int _baseActionPoint;

	private TableValeurs _tableValeurs;

	// private int _currentPlayer;
	private GroupPlayer _groupPlayer;
	private GroupIterator _currentItPlayer;
	private Player _currentPlayer;

	/**
	 * Constructeur
	 * 
	 * @param nbPlayerMax
	 */
	public Game(int nbPlayerMax) {
		initialisation(nbPlayerMax);
	}

	/**
	 * Initialise une partie
	 * 
	 * @param nbPlayerMax
	 */
	private void initialisation(int nbPlayerMax) {

		_id = ID_GAME;
		ID_GAME++;
		_nbTilesX = 20;
		_nbTilesY = 20;
		_baseActionPoint = 10;
		_groupPlayer = new GroupPlayer(nbPlayerMax);
		_currentItPlayer = _groupPlayer.getIterator();
		_tableValeurs = new TableValeurs();
		_map = new Map(_nbTilesX, _nbTilesY);
	}

	/**
	 * @return ID de la game
	 */
	public int getId() {
		return _id;
	}

	/**
	 * @return si la partie est pleine
	 */
	public boolean isFull() {
		return _groupPlayer.isFull();
	}

	/**
	 * Demarre la partie
	 */
	public void start() {
		_groupPlayer.initPlayerPosition(_nbTilesX, _nbTilesY);

		sendStartMessage();

		_currentPlayer.sendMessage("goOK");
	}

	/**
	 * Fait avancer la partie au joueur suivant
	 */
	public void nextTurn() {
		_currentPlayer = _currentItPlayer.nextTurn();
	}

	/**
	 * Permet d'envoyer le message de position de chaque joueur sur la grille
	 */
	public void sendStartMessage() {
		String ms = "startOK";

		for (Player localPlayer : _groupPlayer.getPlayerList()) {
			String msg = ms + localPlayer.getLegionsPosition();
			localPlayer.sendMessage(msg);

			for (Player otherPlayer : _groupPlayer.getPlayerList()) {
				if (otherPlayer.getId() != localPlayer.getId()) {
					msg = "opponentPosition" + otherPlayer.getLegionsPosition();
					localPlayer.sendMessage(msg);
				}
			}

			for (int i = 0; i < _nbTilesX; i++) {
				for (int j = 0; j < _nbTilesY; j++) {
					Tuile tuile = _map.findTuile(i, j);
					System.out.println("i: " + i + " ... j: " + j
							+ "   COLOR: " + tuile.getColorCode());
					localPlayer.sendMessage("mapTuile " + i + " " + j + " "
							+ tuile.getColorCode());
				}
			}
			localPlayer.sendAP();			
		}
	}

	/**
	 * Envoi un message pour enlever une legion
	 * 
	 * @param idLegion
	 */
	public void sendRemoveLegionMessage(int idLegion) {
		String msg = "remove " + idLegion;

		for (Player player : _groupPlayer.getPlayerList())
			player.sendMessage(msg);
	}

	/**
	 * @param connection
	 * @return String qui permet au serveur de dire que le joueur à été ajouté
	 *         avec succès
	 */
	public String newPlayer(Connection connection) {
		_groupPlayer.addPlayer(connection, createDefaultLegionVector(),
				_baseActionPoint);
		_currentItPlayer = _groupPlayer.getIterator();

		_currentPlayer = _currentItPlayer.next();
		return "subscribedOK " + this.getId() + " "
				+ _groupPlayer.getLastPlayer().getId();
	}

	/**
	 * Permet le deplacement d'un player
	 * 
	 * @param legionId
	 * @param x
	 * @param y
	 * @throws ActionPointException
	 */
	public boolean move(int legionId, int x, int y) throws ActionPointException {
		Legion l = null;
		for (Player player : _groupPlayer.getPlayerList()) {
			if (player.hasLegionNextTuile(x, y) != null) {
				l = player.hasLegionNextTuile(x, y);
				break;
			}
		}

		if (l == null)
			_currentPlayer.move(_tableValeurs, nextTuileID(x, y), legionId, x,
					y);
		else {
			combat(legionId, l.getId());
			return false;
		}
		
		_currentPlayer.sendLegionInfos(legionId);
		return true;

	}

	/**
	 * Donne le terrainID de la prochaine tuile a visiter
	 * 
	 * @param x
	 * @param y
	 * @return
	 * @throws ActionPointException
	 */
	public int nextTuileID(int x, int y) throws ActionPointException {
		int terrainID = _map.findTuile(x, y).getTerrainId();
		if (terrainID == Tuile.LAVE)
			throw new ActionPointException();
		return terrainID;
	}

	/**
	 * Envoi un message à l'adversaire
	 * 
	 * @param ms
	 */
	public void sendOpponentMessage() {
		GroupIterator itP = _groupPlayer.getIterator();
		while (itP.hasNext()) {
			Player otherPlayer = itP.next();
			if (otherPlayer.getId() != _currentPlayer.getId()) {
				String ms = "opponentPosition"
						+ _currentPlayer.getLegionsPosition();
				otherPlayer.sendMessage(ms);
			}
		}
	}

	/**
	 * Permet le combat entre 2 legions
	 * 
	 * @param idLegion1
	 * @param idLegion2
	 */
	public void combat(int idLegion1, int idLegion2)
			throws ActionPointException {
		// Recherche des legions
		Legion legion1 = null;
		Legion legion2 = null;

		if (!_currentPlayer.hasLegion(idLegion1))
			return;

		_currentPlayer.canFight(_tableValeurs);

		for (Player player : _groupPlayer.getPlayerList()) {
			if (player.hasLegion(idLegion2))
				legion2 = player.getLegion(idLegion2);
		}

		legion1 = _currentPlayer.getLegion(idLegion1);

		assert (legion1 != null && legion2 != null);

		Tuile battlegroundLegion1 = _map.findTuile(legion2.getX(),
				legion2.getY());

		// effectuer combat

		Combat.fightOneRound(_tableValeurs, legion1, legion2,
				battlegroundLegion1);

		// Enlever les legions mortes

		// Enlever joueur si plus de legions

		// Fin de partie si plus de joueurs

		// sendRemoveLegionMessage(idDeadLegion);
	}

	/**
	 * Vérifie les conditions de victoire
	 */
	public void verifyWinConditions() {
		if (_groupPlayer.nbPlayerGroup() == 1)
			_groupPlayer.getPlayerList().firstElement().sendMessage("Victory");
	}

	/**
	 * Enlève les joueurs du vecteur de jouer a enlever
	 * 
	 * @param playersToRemove
	 */
	public void removePlayers(Vector<Player> playersToRemove) {
		for (Player p : playersToRemove)
			remove(p.getId());
	}

	/**
	 * Enleve le joueur de la partie et appel les conditions pour la victoire
	 * 
	 * @param pId
	 */
	public void remove(int pId) {
		Player p = _groupPlayer.removeFromGroup(pId);
		p.sendMessage("Defeat");
		verifyWinConditions();
	}

	/*************** Gestion des joueurs *************/

	/**********************************************/

	/**
	 * @return un vecteur de legion pour le jouer (par defaut)
	 */
	private Vector<Legion> createDefaultLegionVector() {
		/**** Create unit vector ****/
		Vector<Unit> vu = new Vector<Unit>();
		for (int i = 0; i < rand(2, 10); i++)
			vu.add(createDefaultUnit());

		/**** Create legion vector ****/
		Vector<Legion> vl = new Vector<Legion>();
		Legion l = new Legion(vu);
		vl.add(l);
		return vl;
	}

	/* A CHANGER POUR CORRESPONDRE AVEC LA CRÉATION D'UNITÉS */
	/**
	 * @return creer les unites par defaut
	 */
	private Unit createDefaultUnit() {
		return FactoryUnit.CreateUnit(rand(0, 2), createDefaultAttributeMap(),
				0);
	}

	/**
	 * @return les attributs pour les unites par defaut
	 */
	private AttributeMap createDefaultAttributeMap() {
		return new AttributeMap(rand(2, 10), rand(2, 10), rand(2, 10), rand(2,
				10), rand(2, 10), rand(2, 10), rand(2, 10), rand(2, 10), rand(
				2, 10), rand(2, 10), rand(2, 10), rand(2, 10), rand(2, 10),
				rand(2, 10));
	}

	/**
	 * @param n
	 * @param m
	 * @return un nombre random
	 */
	private int rand(int n, int m) {
		Random r = new Random();
		return (int) Math.round(r.nextGaussian() * n + m);
	}
}
